#include "helpwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QToolButton>
#include <QComboBox>
#include <QLineEdit>
#include <QUrl>
#include <QTextBrowser>
#include <QCloseEvent>
#include <QListView>
#include <QStringListModel>
#include <QDir>
#include <QModelIndex>
#include <QApplication>

HelpWidget *HelpWidget::m_instance = NULL;
QMap<QString, QStringList> *HelpWidget::cHelpNames = NULL;
QMap<QString, QStringList> *HelpWidget::pasHelpNames = NULL;
const QString HelpWidget::CORCPPSTRING = "C/C++";
const QString HelpWidget::PASCALSTRING = "Pascal";
const QString HelpWidget::ALLSTRING = tr("All");

HelpWidget::HelpWidget(QWidget* parent)
    : QWidget(parent)
{
    setupUi(this);
    QList<int> wlist;
    wlist << width()/10 << width()*9/10;
    splitter->setSizes(wlist);
    m_addrLine = comboAddress->lineEdit();
    m_addrLine->setReadOnly(false);
    searchKey = QString();
    language = LG_ALL;
    QStringList lg;
    lg << ALLSTRING << CORCPPSTRING << PASCALSTRING;
    comboLanguage->addItems(lg);
    model = new HelpNamesModel(this);
    listHelpNames->setModel(model);
    displayHelpFileNames();
    connect(toolBack, SIGNAL(clicked()), this, SLOT(slotBack()));
    connect(toolForward, SIGNAL(clicked()), this, SLOT(slotForward()));
    connect(toolRefresh, SIGNAL(clicked()), this, SLOT(slotRefresh()));
    connect(toolHome, SIGNAL(clicked()), this, SLOT(slotHome()));
    connect(toolEnter, SIGNAL(clicked()), this, SLOT(slotAddrReturnPressed()));
    connect (m_addrLine, SIGNAL(returnPressed()),
            this, SLOT(slotAddrReturnPressed()));
    connect(browserHelp, SIGNAL(backwardAvailable(bool)),
            toolBack, SLOT(setEnabled(bool)));
    connect(browserHelp, SIGNAL(forwardAvailable(bool)),
            toolForward, SLOT(setEnabled(bool)));
    connect(browserHelp, SIGNAL(anchorClicked(QUrl)),
            this, SLOT(slotSetUrlSource(QUrl)));
    connect(lineSearch, SIGNAL(textChanged(QString)),
             this, SLOT(slotSearch(QString)));
    connect(lineSearch, SIGNAL(returnPressed()),
            this, SLOT(slotSearch()));
    connect(comboLanguage, SIGNAL(currentIndexChanged(QString)),
            this, SLOT(slotChange(QString)));
    connect(listHelpNames, SIGNAL(clicked(QModelIndex)),
            this, SLOT(displayHelpFile(QModelIndex)));
    connect(listHelpNames, SIGNAL(activated(QModelIndex)),
            this, SLOT(displayHelpFile(QModelIndex)));
#ifdef Q_OS_WIN
    QDir dir = QDir(qApp->applicationDirPath()+"/doc/doc/");
    QString helproot = dir.absolutePath () + "/index.html";
    helproot = QString("file:///") + helproot;
#else
    QString helproot = "file:///usr/share/doc/GUIDE/html/index.html";
#endif
    slotSetSource(helproot);
}

HelpWidget *HelpWidget::instance()
{
    if ( m_instance == NULL )
        m_instance = new HelpWidget;
    return m_instance;
}

bool HelpWidget::hasInstance()
{
    return ( m_instance != NULL );
}

void HelpWidget::slotBack()
{
    //m_browser->backward();
    browserHelp->backward();
    //m_addrLine->setText(m_browser->source().path());
    m_addrLine->setText(browserHelp->source().path());
}

void HelpWidget::slotForward()
{
    //m_browser->forward();
    //m_addrLine->setText(m_browser->source().path());
    browserHelp->forward();
    m_addrLine->setText(browserHelp->source().path());
}

void HelpWidget::slotRefresh()
{
    //m_browser->reload();
    browserHelp->reload();
}

void HelpWidget::slotHome()
{
    //m_browser->home();
    //m_addrLine->setText(m_browser->source().path());
    browserHelp->home();
    m_addrLine->setText(browserHelp->source().path());
}

void HelpWidget::slotSetUrlSource(QUrl url)
{
    QString urlStr = url.toString();
    slotSetSource(urlStr);
}

void HelpWidget::slotSetSource(QString &url)
{
    m_addrLine->setText(url);
    recordUrl(url);
    //m_browser->setSource(QUrl(url));
    browserHelp->setSource(QUrl(url));
    this->activateWindow();
}

void HelpWidget::slotSearch()
{
    //slotSearch(searchLine->text());
    slotSearch(lineSearch->text());
}

void HelpWidget::slotSearch(const QString &key)
{
    //searchLine->setText(key);
    lineSearch->setText(key);
    searchKey = key;
    displayHelpFileNames();
    if (!key.isEmpty()) {
        displayHelpFile(model->index(0));
        //helpFiles->setCurrentIndex(model->index(0));
        listHelpNames->setCurrentIndex(model->index(0));
    }
}

void HelpWidget::slotChange(QString lg)
{
    if (lg == CORCPPSTRING)
        language = LG_CORCPP;
    else if (lg == PASCALSTRING)
        language = LG_PASCAL;
    else if (lg == ALLSTRING)
        language = LG_ALL;
    else
        language = LG_NONE;
    displayHelpFileNames();
}

void HelpWidget::closeEvent ( QCloseEvent * event )
{
    event->accept();
}

void HelpWidget::slotAddrReturnPressed()
{
    QString addr = m_addrLine->text();
    if (addr.isEmpty())
        return;
    QUrl url(addr);
    if (!url.isValid()) {
        m_addrLine->selectAll();
        return;
    }
    slotSetSource(addr);
}

void HelpWidget::recordUrl(QString &addr)
{
    //m_address->insertItem(0, addr);
    comboAddress->insertItem(0, addr);
}

void HelpWidget::displayHelpFileNames()
{
    model->setKey(searchKey, language);
}

void HelpWidget::displayHelpFile(QModelIndex index)
{
    QString path;
    QString filename = index.data().toString() + ".html";
    if (index.data(Qt::UserRole).toString() == "C/C++")
        path = getCHelpPath().absolutePath() + "/" + filename;
    else
        path = getPasHelpPath().absolutePath() + "/" + filename;
    QFile file(path);
    if (file.open(QIODevice::ReadOnly)) {
        QTextStream in(&file);
        QString line = in.readLine().trimmed();
        while (!line.isEmpty()) {
            if (line.startsWith("location.href=")) {
                path = path.section('/', 0, -2) + "/" + line.section('\"', 1, 1);
                break;
            }
            line = in.readLine().trimmed();
        }
    }
    QString url = "file:///" + QDir::cleanPath(path);
    slotSetSource(url);
}

QDir HelpWidget::getCHelpPath()
{
    QDir dir;
#ifdef Q_OS_WIN
    dir = QDir(qApp->applicationDirPath());
    dir.cd("./doc/man.html");
#else
    dir = QDir("/usr/share/doc/GUIDE/man.html");
#endif
    return dir;
}

QDir HelpWidget::getPasHelpPath()
{
    QDir dir;
#ifdef Q_OS_WIN
    dir = QDir(qApp->applicationDirPath());
    dir.cd("./doc/rtl.html");
#else
    dir = QDir("/usr/share/doc/GUIDE/rtl.html");
#endif
    return dir;
}

void HelpWidget::iniCHelpNames()
{
    cHelpNames = new QMap<QString, QStringList>;
    QDir cDir = getCHelpPath();
    QStringList folders = cDir.entryList();
    if (!folders.isEmpty()) {
        folders.removeFirst();
        folders.removeFirst();
    }
    QDir dir;
    QStringList sl;
    foreach(QString s, folders) {
        dir = cDir;
        if (!QFileInfo(dir.absolutePath() + "/" + s).isDir()) {
            if (s.endsWith(".html"))
                cHelpNames->insert(s.section('.', 0, 0), QStringList());
            continue;
        }
        dir.cd(s);
        sl = dir.entryList();
        if (!sl.isEmpty()) {
            sl.removeFirst();
            sl.removeFirst();
            sl.replaceInStrings(".html", "");
            cHelpNames->insert(s, sl);
        }
    }
}

const QMap<QString, QStringList> *HelpWidget::getCHelpNames()
{
    return cHelpNames;
}

void HelpWidget::iniPasHelpNames()
{
    pasHelpNames = new QMap<QString, QStringList>;
    QDir pasDir = getPasHelpPath();
    QStringList folders = pasDir.entryList();
    if (!folders.isEmpty()) {
        folders.removeFirst();
        folders.removeFirst();
    }
    QDir dir;
    QStringList sl;
    foreach(QString s, folders) {
        dir = pasDir;
        if (!QFileInfo(dir.absolutePath() + "/" + s).isDir()) {
            if (s.endsWith(".html"))
                pasHelpNames->insert(s.section('.', 0, 0), QStringList());
            continue;
        }
        dir.cd(s);
        sl = dir.entryList();
        if (!sl.isEmpty()) {
            sl.removeFirst();
            sl.removeFirst();
            sl.replaceInStrings(".html", "");
            pasHelpNames->insert(s, sl);
        }
    }
}

const QMap<QString, QStringList> *HelpWidget::getPasHelpNames()
{
    return pasHelpNames;
}

HelpNamesModel::HelpNamesModel ( QObject *p )
    :QAbstractListModel (p)
{
}

HelpNamesModel::HelpNamesModel ( const QString & k, const HelpWidget::LANGUAGE &lg, QObject * parent )
    :QAbstractListModel ( parent)
{
    setKey(k, lg);
}

QVariant HelpNamesModel::data(const QModelIndex &index, int role) const
{
    if (index.row() < 0 || index.row() >= lst.size())
        return QVariant();
    QString n = lst.at(index.row()).getName();
    HelpWidget::LANGUAGE l = lst.at(index.row()).getLanguage();
    if (role == Qt::DisplayRole)
        return n;

    if (role == Qt::UserRole) {
        if (l == HelpWidget::LG_CORCPP)
            return QVariant("C/C++");
        else if (l == HelpWidget::LG_PASCAL)
            return QVariant("Pascal");
        else
            return QVariant();
    }

    if (role == Qt::ToolTipRole) {
        if (l == HelpWidget::LG_CORCPP)
            return QVariant("C/C++\n"+n);
        else if (l == HelpWidget::LG_PASCAL)
            return QVariant("Pascal\n"+n);
        else
            return QVariant();

    }
    return QVariant();
}

int HelpNamesModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;

    return lst.count();
}

Qt::ItemFlags HelpNamesModel::flags(const QModelIndex &index) const
{
    return QAbstractItemModel::flags(index);
}

bool HelpNamesModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if (count < 1 || row < 0 || row > rowCount(parent))
        return false;

    beginInsertRows(QModelIndex(), row, row + count - 1);

    for (int r = 0; r < count; ++r)
        lst.insert(row, HelpName(QString(), HelpWidget::LG_NONE));

    endInsertRows();

    return true;
}

bool HelpNamesModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if (count <= 0 || row < 0 || (row + count) > rowCount(parent))
        return false;

    beginRemoveRows(QModelIndex(), row, row + count - 1);

    for (int r = 0; r < count; ++r)
        lst.removeAt(row);

    endRemoveRows();

    return true;
}

static bool ascendingLessThan(const QPair<HelpName, int> &s1,
                              const QPair<HelpName, int> &s2)
{
    return s1.first.getName() < s2.first.getName();
}

static bool decendingLessThan(const QPair<HelpName, int> &s1,
                              const QPair<HelpName, int> &s2)
{
    return s1.first.getName() > s2.first.getName();
}

void HelpNamesModel::sort(int, Qt::SortOrder order)
{
    emit layoutAboutToBeChanged();

    QList<QPair<HelpName, int> > list;
    for (int i = 0; i < lst.count(); ++i)
        list.append(QPair<HelpName, int>(lst.at(i), i));

    if (order == Qt::AscendingOrder)
        qSort(list.begin(), list.end(), ascendingLessThan);
    else
        qSort(list.begin(), list.end(), decendingLessThan);

    lst.clear();
    QVector<int> forwarding(list.count());
    for (int i = 0; i < list.count(); ++i) {
        lst.append(list.at(i).first);
        forwarding[list.at(i).second] = i;
    }

    QModelIndexList oldList = persistentIndexList();
    QModelIndexList newList;
    for (int i = 0; i < oldList.count(); ++i)
        newList.append(index(forwarding.at(oldList.at(i).row()), 0));
    changePersistentIndexList(oldList, newList);

    emit layoutChanged();
}

void HelpNamesModel::setKey(const QString &k, const HelpWidget::LANGUAGE &l)
{
    emit beginResetModel();
    lst.clear();
    const QMap<QString,QStringList> *c = HelpWidget::getCHelpNames();
    const QMap<QString,QStringList> *p = HelpWidget::getPasHelpNames();
    if (l == HelpWidget::LG_ALL || l == HelpWidget::LG_CORCPP) {
        for (QMap<QString, QStringList>::ConstIterator i = c->begin();
             i != c->end(); i++) {
            foreach (QString s, i.value()) {
                if (s.contains(k, Qt::CaseInsensitive)) {
                    lst.append(HelpName(i.key() + '/' + s, HelpWidget::LG_CORCPP));
                }
            }
        }
    }
    if (l == HelpWidget::LG_ALL || l == HelpWidget::LG_PASCAL) {
        for (QMap<QString, QStringList>::ConstIterator i = p->begin();
             i != p->end(); i++) {
            foreach (QString s, i.value()) {
                if (s.contains(k, Qt::CaseInsensitive)) {
                    lst.append(HelpName(i.key() + "/" + s, HelpWidget::LG_PASCAL));
                }
            }
        }
    }
    emit endResetModel();
}

HelpName::HelpName(QString s, HelpWidget::LANGUAGE l)
    : name(s), language(l)
{}
